use crate::models::order_items::{self, CreateParams};
use axum::{
    Json, Router,
    extract::State,
    routing::{get, post},
};
use loco_rs::prelude::*;
use serde::Deserialize;

pub fn routes() -> Router<AppContext> {
    Router::new()
        .route("/order-items", post(create))
        .route("/order-items/:id", get(get_by_id))
        .route("/order-items/:id/quantity", post(update_quantity))
        .route("/order-items/:id/price", post(update_price))
        .route("/order-items/:id/delete", post(delete))
        .route("/order-items/by-order/:order_id", get(get_by_order))
        .route(
            "/order-items/by-order-number/:order_number",
            get(get_by_order_number),
        )
}

async fn create(
    State(state): State<AppContext>,
    Json(params): Json<CreateParams>,
) -> Result<Response> {
    let order_item = order_items::Model::create(&state.db, &params).await?;
    format::json(order_item)
}

async fn get_by_id(
    State(state): State<AppContext>,
    axum::extract::Path(id): axum::extract::Path<String>,
) -> Result<Response> {
    let order_item = order_items::Model::find_by_id(&state.db, &id).await?;
    format::json(order_item)
}

#[derive(Debug, Deserialize)]
pub struct UpdateQuantityParams {
    pub quantity: i32,
}

async fn update_quantity(
    State(state): State<AppContext>,
    axum::extract::Path(id): axum::extract::Path<String>,
    Json(params): Json<UpdateQuantityParams>,
) -> Result<Response> {
    let order_item = order_items::Model::find_by_id(&state.db, &id).await?;
    let active_model: order_items::ActiveModel = order_item.into();
    let updated_item = active_model
        .update_quantity(&state.db, params.quantity)
        .await?;
    format::json(updated_item)
}

#[derive(Debug, Deserialize)]
pub struct UpdatePriceParams {
    pub price: rust_decimal::Decimal,
}

async fn update_price(
    State(state): State<AppContext>,
    axum::extract::Path(id): axum::extract::Path<String>,
    Json(params): Json<UpdatePriceParams>,
) -> Result<Response> {
    let order_item = order_items::Model::find_by_id(&state.db, &id).await?;
    let active_model: order_items::ActiveModel = order_item.into();
    let updated_item = active_model.update_price(&state.db, params.price).await?;
    format::json(updated_item)
}

async fn delete(
    State(state): State<AppContext>,
    axum::extract::Path(id): axum::extract::Path<String>,
) -> Result<Response> {
    let order_item = order_items::Model::find_by_id(&state.db, &id).await?;
    let deleted_item = order_item.soft_delete(&state.db).await?;
    format::json(deleted_item)
}

async fn get_by_order(
    State(state): State<AppContext>,
    axum::extract::Path(order_id): axum::extract::Path<String>,
) -> Result<Response> {
    let order_items = order_items::Model::find_by_order_id(&state.db, &order_id).await?;
    format::json(order_items)
}

async fn get_by_order_number(
    State(state): State<AppContext>,
    axum::extract::Path(order_number): axum::extract::Path<String>,
) -> Result<Response> {
    let order_items = order_items::Model::find_by_order_number(&state.db, &order_number).await?;
    format::json(order_items)
}
